<?php
	/**
	  * @package	MyDebuger
	  * @name		CombinationsBuilder
	  * @brief		Clase para generar combinaciones de una lista de elementos
	  * @author		Hermann D. Schimpf <hschimpf@gschimpf.com>
	  * @license	http://opensource.org/licenses/gpl-2.0.php
	  * @date		2009-12-21
	  * @version	0.1.1
  	**/

	class CombinationsBuilder extends MyDebuger implements MyVersions {
		/* version de la clase */
		private static $version			= 0;
		/* subversion de la clase */
		private static $subversion		= 1;
		/* numero de revision de la clase */
		private static $revision		= 1;

		/* elementos a procesar */
		private $elementsList					= Array();
		/* array donde almacenaremos las combinaciones */
		private $combinaciones					= Array();
		/* array temporal de proceso */
		private $combinacion						= Array();

		public function __construct($debug = False) {
			/* creamos el padre */
			parent::__construct($debug, __CLASS__, 086);
			/* habilitamos el registro en un fichero */
			$this->enableLog(__CLASS__ . '.log');
		}

		public static final function getVersion() {
			/* retornamos la version actual de la clase */
			return 'v' . self::$version . '.' . self::$subversion . '.' . self::$revision;
		}

		public static function getCombinations($elementsList = Null, $debug = False) {
			try {
				try {
					/* creamos una instancia */
					$getCombinations = new CombinationsBuilder($debug);
					/* verificamos los valores recibidos */
					$getCombinations->verificarValoresRecibidos($elementsList);
					/* almacenamos los elementos recibidos */
					$getCombinations->saveElements($elementsList);
					/* armamos las combinaciones */
					$getCombinations->armarCombinaciones();
					/* retornamos las combinaciones */
					return $getCombinations->getCombinaciones();
				} catch (Exception $e) {
					/* capturamos todos los errores y los transformamos en mi excepcion */
					$getCombinations->convertToMyException($e);
				}
			} catch (MyException $e) {
				/* si se produjo una excepcion la manejamos con el metodo 'show' */
				$getCombinations->show($e->getMensaje());
			}
		}

		private function verificarValoresRecibidos($elementsList) {
			/* verificamos si especifico algun valor */
			if ($elementsList === Null)
				/* salimos con una excepcion */
				$this->newException('No se especificaron los elementos a procesar');
			/* verificamos si los elementos se pasaron por medio de un array */
			if (gettype($elementsList) !== 'array')
				/* salimos con una excepcion */
				$this->newException('Los elementos se deben enviar en un array');
		}

		private function saveElements($elementsList) {
			/* almacenamos los elementos recibidos */
			$this->elementsList = $elementsList;
		}

		private function getElements() {
			/* retornamos los elementos */
			return $this->elementsList;
		}

		private function agregarCombinacion($combinacion) {
			/* almacenamos la combinacion actual */
			$this->combinaciones[] = $combinacion;
		}

		private function addElement($index, $element) {
			/* almacenamos el elemento en el indice */
			$this->combinacion[$index] = $element;
		}

		private function getCombinacion() {
			/* retornamos la combinacion actual */
			return $this->combinacion;
		}

		private function getCombinaciones() {
			/* retornamos las combinaciones actuales */
			return $this->combinaciones;
		}

		private function armarCombinaciones() {
			/* almacenamos la combinacion con los elementos en el orden actual */
			$this->agregarCombinacion($this->getElements());
			/* recorremos los elementos */
			foreach ($this->getElements() AS $element)
				/* procesamos el elemento actual */
				$this->processElement($element);
		}

		private function processElement($element) {
			/* ingresamos a un bucle por la cantidad total de elementos */
			for ($i = 0; $i < count($this->getElements()); $i++) {
				/* agregamos el elemento actual en la posicion actual */
				$this->addElement($i, $element);
				/* recorremos los elementos */
				foreach ($this->getElements() AS $missingElement)
					/* procesamos el elemento faltante */
					$this->processMissingElement($missingElement);
				/* ordenamos las combinaciones */
				$this->ordenarCombinacion();
				/* verificamos si la combinacion ya la tenemos */
				if (!in_array($this->getCombinacion(), $this->getCombinaciones()))
					/* creamos una combinacion con el elemento actual */
					$this->agregarCombinacion($this->getCombinacion());
			}
		}

		private function processMissingElement($missingElement) {
			/* verificamos si el elemento ya existe en la combinacion */
			if (!in_array($missingElement, $this->getCombinacion()))
				/* agregamos el elemento faltante */
				$this->addMissingElement($missingElement);
		}

		private function addMissingElement($missingElement) {
			/* recorremos las posiciones de la combinacion */
			for ($i = 0; $i < count($this->getElements()); $i++) {
				/* verificamos si la posicion esta en uso */
				if (!$this->existeCombinacion($i)) {
					/* agregamos el elemento */
					$this->addElement($i, $missingElement);
					/* finalizamos el buqle */
					break;
				}
			}
		}

		private function existeCombinacion($index) {
			/* retornamos si existe el indice */
			return isset($this->combinacionTemporal[$index]);
		}

		private function ordenarCombinacion() {
			/* ordenamos el array */
			ksort($this->combinacion);
		}
	}
?>